babl: add parametric handling for sRGB style TRCs (icc V4)
authorØyvind Kolås <pippin@gimp.org>
Sat, 2 Sep 2017 15:09:20 +0000 (17:09 +0200)
committerØyvind Kolås <pippin@gimp.org>
Sat, 2 Sep 2017 15:10:01 +0000 (17:10 +0200)
babl/babl-icc.c
babl/babl-internal.h
babl/babl-trc.c
babl/babl-trc.h

index 5ae66bc0352b73f02616bbe7b28d0ae75dd455f1..debbc6905e1caefaedae7005fe6ad81ee2e44c52 100644 (file)
@@ -315,22 +315,45 @@ static const Babl *babl_trc_from_icc (ICC  *state, int offset,
               g = icc_read (s15f16, offset + 12 + 2 * 0);
               return babl_trc_gamma (g);
               break;
-
             case 3:
-#if 0
-         float a,b,c,d,e,f;
-              g = icc_read (s15f16, offset + 12 + 2 * 0);
-              a = icc_read (s15f16, offset + 12 + 2 * 1);
-              b = icc_read (s15f16, offset + 12 + 2 * 2);
-              c = icc_read (s15f16, offset + 12 + 2 * 3);
-              d = icc_read (s15f16, offset + 12 + 2 * 4);
-              e = icc_read (s15f16, offset + 12 + 2 * 5);
-              f = icc_read (s15f16, offset + 12 + 2 * 6);
-#endif
-              return babl_trc ("sRGB"); // XXX: not true... and I suspect the CIE L* curve might be expressed as this,
-                                        // formula as well, depending on arguments.
+              {
+                float a,b,c,d;
+                g = icc_read (s15f16, offset + 12 + 2 * 0);
+                a = icc_read (s15f16, offset + 12 + 2 * 1);
+                b = icc_read (s15f16, offset + 12 + 2 * 2);
+                c = icc_read (s15f16, offset + 12 + 2 * 3);
+                d = icc_read (s15f16, offset + 12 + 2 * 4);
+                //fprintf (stderr, "%f %f %f %f %f\n", g, a, b, c, d);
+                if (fabs (g - 2.40)     < 0.01 &&
+                    fabs (a - 26214)    < 0.01 &&
+                    fabs (b - 0.947875) < 0.01 &&
+                    fabs (c - (-3417))  < 0.01)
+                  return babl_trc ("sRGB");
+                else
+                  return babl_trc_formula_srgb (g, a, b, c, d);
+              }
+              break;
+            case 4:
+              {
+                float a,b,c,d,e,f;
+                g = icc_read (s15f16, offset + 12 + 2 * 0);
+                a = icc_read (s15f16, offset + 12 + 2 * 1);
+                b = icc_read (s15f16, offset + 12 + 2 * 2);
+                c = icc_read (s15f16, offset + 12 + 2 * 3);
+                d = icc_read (s15f16, offset + 12 + 2 * 4);
+                e = icc_read (s15f16, offset + 12 + 2 * 5);
+                f = icc_read (s15f16, offset + 12 + 2 * 6);
+                fprintf (stderr, "%f %f %f %f %f %f %f\n",
+                              g, a, b, c, d, e, f);
+            {
+              fprintf (stdout, "unhandled parametric sRGB formula TRC type %i\n", function_type);
+              *error = "unhandled sRGB formula like TRC";
+              return babl_trc_gamma (2.2);
+            }
+                              }
               break;
             default:
+              *error = "unhandled parametric TRC";
               fprintf (stdout, "unhandled parametric TRC type %i\n", function_type);
               return babl_trc_gamma (2.2);
             break;
@@ -399,14 +422,42 @@ switch (trc->type)
     icc_write (u32, state->o + 4, 0);
     icc_write (u32, state->o + 8, 0);
     break;
-  case BABL_TRC_GAMMA:
+  case BABL_TRC_FORMULA_GAMMA:
     icc_allocate_tag (state, name, 14);
     icc_write (sign, state->o, "curv");
     icc_write (u32, state->o + 4, 0);
     icc_write (u32, state->o + 8, 1);
     icc_write (u8f8, state->o + 12, trc->gamma);
     break;
+  case BABL_TRC_GAMMA_1_8:
+    icc_allocate_tag (state, name, 14);
+    icc_write (sign, state->o, "curv");
+    icc_write (u32, state->o + 4, 0);
+    icc_write (u32, state->o + 8, 1);
+    icc_write (u8f8, state->o + 12, 1.8);
+    break;
+  case BABL_TRC_GAMMA_2_2:
+    icc_allocate_tag (state, name, 14);
+    icc_write (sign, state->o, "curv");
+    icc_write (u32, state->o + 4, 0);
+    icc_write (u32, state->o + 8, 1);
+    icc_write (u8f8, state->o + 12, 2.2);
+    break;
+  case BABL_TRC_LUT:
+    icc_allocate_tag (state, name, 13 + trc->lut_size * 2);
+    icc_write (sign, state->o, "curv");
+    icc_write (u32, state->o + 4, 0);
+    icc_write (u32, state->o + 8, trc->lut_size);
+    {
+      int j;
+      for (j = 0; j < trc->lut_size; j ++)
+        icc_write (u16, state->o + 12 + j * 2, (int)(trc->lut[j]*65535.5f));
+    }
+    break;
+  // this is the case catching things not directly representable in v2
   case BABL_TRC_SRGB:
+  case BABL_TRC_FORMULA_SRGB:
+//  default:
     {
       int lut_size = 512;
       icc_allocate_tag (state, name, 13 + lut_size * 2);
@@ -417,28 +468,9 @@ switch (trc->type)
         int j;
         for (j = 0; j < lut_size; j ++)
         icc_write (u16, state->o + 12 + j * 2,
-            gamma_2_2_to_linear (j / (lut_size-1.0)) * 65535.5);
+            babl_trc_to_linear ((void*)trc, j / (lut_size-1.0)) * 65535.5);
       }
     }
-    break;
-  case BABL_TRC_LUT:
-    icc_allocate_tag (state, name, 13 + trc->lut_size * 2);
-    icc_write (sign, state->o, "curv");
-    icc_write (u32, state->o + 4, 0);
-    icc_write (u32, state->o + 8, trc->lut_size);
-    {
-      int j;
-      for (j = 0; j < trc->lut_size; j ++)
-        icc_write (u16, state->o + 12 + j * 2, (int)(trc->lut[j]*65535.5f));
-    }
-    break;
-  default:
-    icc_allocate_tag (state, name, 14);
-    icc_write (sign, state->o, "curv");
-    icc_write (u32, state->o + 4, 0);
-    icc_write (u32, state->o + 8, 1);
-    icc_write (u8f8, state->o + 12, trc->gamma);
-    break;
 }
 }
 
index 440f7e87dc7b36c9fdb2936b58cdc2f78ee269ce..c7c87cf9f67ebee40710172b6d2702453a2bc88a 100644 (file)
@@ -362,5 +362,7 @@ int babl_list_destroy (void *data);
 const char *
 babl_conversion_create_name (Babl *source, Babl *destination, int is_reference);
 void _babl_space_add_universal_rgb (const Babl *space);
+const Babl *
+babl_trc_formula_srgb (double gamma, double a, double b, double c, double d);
 
 #endif
index b9f1c49e9b91a99a44d5289dfbc772d693119420..4aacdef2f3377abf14cf40849184295eff258826 100644 (file)
@@ -343,12 +343,6 @@ static inline float babl_powf (float x, float y)
   return expf (y * logf (x));
 }
 
-static inline float _babl_trc_gamma_to_linear (const Babl *trc_, float value)
-{
-  BablTRC *trc = (void*)trc_;
-  return babl_powf (value, trc->gamma);
-}
-
 
 static inline void _babl_trc_gamma_to_linear_buf (const Babl *trc_, const float *in, float *out, int in_gap, int out_gap, int components, int count)
 {
@@ -376,6 +370,48 @@ static inline float _babl_trc_gamma_from_linear (const Babl *trc_, float value)
   return babl_powf (value, trc->rgamma);
 }
 
+static inline float _babl_trc_gamma_to_linear (const Babl *trc_, float value)
+{
+  BablTRC *trc = (void*)trc_;
+  return babl_powf (value, trc->gamma);
+}
+
+static inline float _babl_trc_formula_srgb_from_linear (const Babl *trc_, float value)
+{
+  BablTRC *trc = (void*)trc_;
+  float x= value;
+  float g = trc->lut[0];
+  float a = trc->lut[1];
+  float b = trc->lut[2];
+  float c = trc->lut[3];
+  float d = trc->lut[4];
+  if (x > c * d)  // XXX: verify that this math is the correct inverse
+  {
+    float v = babl_powf (x, 1.0/g);
+    v = (v-b)/a;
+    return v;
+  }
+  return x / c;
+}
+
+static inline float _babl_trc_formula_srgb_to_linear (const Babl *trc_, float value)
+{
+  BablTRC *trc = (void*)trc_;
+  float x= value;
+  float g = trc->lut[0];
+  float a = trc->lut[1];
+  float b = trc->lut[2];
+  float c = trc->lut[3];
+  float d = trc->lut[4];
+
+  if (x >= d)
+  {
+    return babl_powf (a * x + b, g);
+  }
+  return c * x;
+}
+
+
 static inline float _babl_trc_gamma_1_8_to_linear (const Babl *trc_, float x)
 {
   if (x >= 0.01f && x < 0.7f)
@@ -691,12 +727,22 @@ babl_trc_new (const char *name,
       trc_db[i].fun_from_linear_buf = _babl_trc_linear_buf;
       trc_db[i].fun_to_linear_buf = _babl_trc_linear_buf;
       break;
-    case BABL_TRC_GAMMA:
+    case BABL_TRC_FORMULA_GAMMA:
       trc_db[i].fun_to_linear = _babl_trc_gamma_to_linear;
       trc_db[i].fun_from_linear = _babl_trc_gamma_from_linear;
       trc_db[i].fun_to_linear_buf = _babl_trc_gamma_to_linear_buf;
       trc_db[i].fun_from_linear_buf = _babl_trc_gamma_from_linear_buf;
       break;
+    case BABL_TRC_FORMULA_SRGB:
+      trc_db[i].lut = babl_calloc (sizeof (float), 5);
+      {
+        int j;
+        for (j = 0; j < 5; j++)
+          trc_db[i].lut[j] = lut[j];
+      }
+      trc_db[i].fun_to_linear = _babl_trc_formula_srgb_to_linear;
+      trc_db[i].fun_from_linear = _babl_trc_formula_srgb_from_linear;
+      break;
     case BABL_TRC_GAMMA_2_2:
       trc_db[i].fun_to_linear = _babl_trc_gamma_2_2_to_linear;
       trc_db[i].fun_from_linear = _babl_trc_gamma_2_2_from_linear;
@@ -738,6 +784,21 @@ babl_trc_class_for_each (BablEachFunction each_fun,
       return;
 }
 
+const Babl *
+babl_trc_formula_srgb (double gamma, double a, double b, double c, double d)
+{
+  char name[32];
+  int i;
+  float params[5]={gamma, a, b, c, d};
+
+  sprintf (name, "%.6f %.6f %.4f %.4f %.4f", gamma, a, b, c, d);
+  for (i = 0; name[i]; i++)
+    if (name[i] == ',') name[i] = '.';
+  while (name[strlen(name)-1]=='0')
+    name[strlen(name)-1]='\0';
+  return babl_trc_new (name, BABL_TRC_FORMULA_SRGB, gamma, 0, params);
+}
+
 const Babl *
 babl_trc_gamma (double gamma)
 {
@@ -755,7 +816,7 @@ babl_trc_gamma (double gamma)
     if (name[i] == ',') name[i] = '.';
   while (name[strlen(name)-1]=='0')
     name[strlen(name)-1]='\0';
-  return babl_trc_new (name, BABL_TRC_GAMMA, gamma, 0, NULL);
+  return babl_trc_new (name, BABL_TRC_FORMULA_GAMMA, gamma, 0, NULL);
 }
 
 void
index 0b7fb705477ef33cc4c442a6a676b6dfac055719..95020b9282a3ab10d26a5789bb540a2dc36bf26d 100644 (file)
 BABL_CLASS_DECLARE (trc);
 
 typedef enum {BABL_TRC_LINEAR,
-              BABL_TRC_GAMMA,
+              BABL_TRC_FORMULA_GAMMA,
               BABL_TRC_GAMMA_1_8,
               BABL_TRC_GAMMA_2_2,
               BABL_TRC_SRGB,
+              BABL_TRC_FORMULA_SRGB,
               BABL_TRC_LUT}
 BablTRCType;